--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Node / ReticulumProjects / MeshChatX.git / files / android / app / src / main / python / meshchat_wrapper.py
Displaying Raw • Download
android/app/src/main/python/meshchat_wrapper.py 4e4ef0526a424b653e48f5720d45765fdd6a4869 (4e4ef052) Text, 11.99 KB
T8b949e# SPDX-License-Identifier: 0BSD
Tff7b72import T7ee787contextlib
Tff7b72import T7ee787os
Tff7b72import T7ee787signal
Tff7b72import T7ee787sys
Tff7b72import T7ee787threading
Tff7b72import T7ee787time
T8b949e# Prevents a second meshchat main() if Java starts two threads (e.g. activity edge cases).
Te6edf3_server_loop_lock Tff7b72= Te6edf3threadingTff7b72.Td2a8ffLockTb4b4b4(Tb4b4b4)
Te6edf3_server_loop_active Tff7b72= Tff7b72False
Tff7b72def Td2a8ff_ensure_android_reticulum_configTb4b4b4(Te6edf3reticulum_config_dirTb4b4b4)Tb4b4b4:
Tff7b72if Tff7b72not Te6edf3reticulum_config_dirTb4b4b4:
Tff7b72return
Te6edf3config_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3reticulum_config_dirTb4b4b4, Ta5d6ff"Ta5d6ffconfigTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3config_pathTb4b4b4)Tb4b4b4:
Tff7b72with Tffa657openTb4b4b4(Te6edf3config_pathTb4b4b4, Te6edf3encodingTff7b72=Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4) Tff7b72as Te6edf3existing_fileTb4b4b4:
Te6edf3content Tff7b72= Te6edf3existing_fileTff7b72.Td2a8ffreadTb4b4b4(Tb4b4b4)
Te6edf3changed Tff7b72= Tff7b72False
Tff7b72if Ta5d6ff"Ta5d6ffshare_instance = YesTa5d6ff" Tff7b72in Te6edf3contentTb4b4b4:
Te6edf3content Tff7b72= Te6edf3contentTff7b72.Td2a8ffreplaceTb4b4b4(Ta5d6ff"Ta5d6ffshare_instance = YesTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffshare_instance = NoTa5d6ff"Tb4b4b4)
Te6edf3changed Tff7b72= Tff7b72True
Tff7b72if Ta5d6ff"Ta5d6ffpanic_on_interface_errorTa5d6ff" Tff7b72not Tff7b72in Te6edf3contentTb4b4b4:
Tff7b72if Ta5d6ff"Ta5d6ff[reticulum]Ta5d6ff" Tff7b72in Te6edf3contentTb4b4b4:
Te6edf3content Tff7b72= Te6edf3contentTff7b72.Td2a8ffreplaceTb4b4b4(
Ta5d6ff"Ta5d6ff[reticulum]Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff[reticulum]Tffea00\nTa5d6ff panic_on_interface_error = NoTa5d6ff"Tb4b4b4,
T79c0ff1Tb4b4b4,
Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3content Tff7b72= Ta5d6ff"Ta5d6ff[reticulum]Tffea00\nTa5d6ff panic_on_interface_error = NoTffea00\nTffea00\nTa5d6ff" Tff7b72+ Te6edf3content
Te6edf3changed Tff7b72= Tff7b72True
Tff7b72if Te6edf3changedTb4b4b4:
Tff7b72with Tffa657openTb4b4b4(Te6edf3config_pathTb4b4b4, Ta5d6ff"Ta5d6ffwTa5d6ff"Tb4b4b4, Te6edf3encodingTff7b72=Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4) Tff7b72as Te6edf3config_fileTb4b4b4:
Te6edf3config_fileTff7b72.Td2a8ffwriteTb4b4b4(Te6edf3contentTb4b4b4)
Tff7b72return
Tff7b72with Tffa657openTb4b4b4(Te6edf3config_pathTb4b4b4, Ta5d6ff"Ta5d6ffwTa5d6ff"Tb4b4b4, Te6edf3encodingTff7b72=Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4) Tff7b72as Te6edf3config_fileTb4b4b4:
Te6edf3config_fileTff7b72.Td2a8ffwriteTb4b4b4(
Ta5d6ff"Ta5d6ff[reticulum]Tffea00\nTa5d6ff"
Ta5d6ff"Ta5d6ff share_instance = NoTffea00\nTa5d6ff"
Ta5d6ff"Ta5d6ff panic_on_interface_error = NoTffea00\nTa5d6ff"
Ta5d6ff"Tffea00\nTa5d6ff"
Ta5d6ff"Ta5d6ff[interfaces]Tffea00\nTa5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72def Td2a8ff_patch_asyncio_signal_handlers_for_androidTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Tff7b72from T7ee787asyncio Tff7b72import Te6edf3unix_events
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72return Tff7b72None
Te6edf3loop_cls Tff7b72= Tffa657getattrTb4b4b4(Te6edf3unix_eventsTb4b4b4, Ta5d6ff"Ta5d6ff_UnixSelectorEventLoopTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)
Tff7b72if Te6edf3loop_cls Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72return Tff7b72None
Te6edf3original_add_signal_handler Tff7b72= Te6edf3loop_clsTff7b72.Td2a8ffadd_signal_handler
Tff7b72def Td2a8ff_safe_add_signal_handlerTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3sigTb4b4b4, Te6edf3callbackTb4b4b4, Tff7b72*Te6edf3argsTb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Tff7b72return Te6edf3original_add_signal_handlerTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3sigTb4b4b4, Te6edf3callbackTb4b4b4, Tff7b72*Te6edf3argsTb4b4b4)
Tff7b72except Tb4b4b4(Tf85149RuntimeErrorTb4b4b4, Tf85149ValueErrorTb4b4b4) Tff7b72as Te6edf3excTb4b4b4:
Te6edf3message Tff7b72= Tffa657strTb4b4b4(Te6edf3excTb4b4b4)
Tff7b72if Ta5d6ff"Ta5d6ffset_wakeup_fd only works in main threadTa5d6ff" Tff7b72in Te6edf3messageTb4b4b4:
Tff7b72return Tff7b72None
Tff7b72if Ta5d6ff"Ta5d6ffmain thread of the main interpreterTa5d6ff" Tff7b72in Te6edf3messageTb4b4b4:
Tff7b72return Tff7b72None
Tff7b72raise
Te6edf3loop_clsTff7b72.Td2a8ffadd_signal_handler Tff7b72= Te6edf3_safe_add_signal_handler
Tff7b72return Te6edf3loop_clsTb4b4b4, Te6edf3original_add_signal_handler
Tff7b72def Td2a8ff_patch_aiohttp_run_app_for_androidTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Tff7b72from T7ee787aiohttp Tff7b72import Te6edf3web
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72return Tff7b72None
Te6edf3original_run_app Tff7b72= Te6edf3webTff7b72.Td2a8ffrun_app
Tff7b72def Td2a8ff_safe_run_appTb4b4b4(Tff7b72*Te6edf3argsTb4b4b4, Tff7b72*Tff7b72*Te6edf3kwargsTb4b4b4)Tb4b4b4:
Te6edf3kwargsTff7b72.Td2a8ffsetdefaultTb4b4b4(Ta5d6ff"Ta5d6ffhandle_signalsTa5d6ff"Tb4b4b4, Tff7b72FalseTb4b4b4)
Tff7b72return Te6edf3original_run_appTb4b4b4(Tff7b72*Te6edf3argsTb4b4b4, Tff7b72*Tff7b72*Te6edf3kwargsTb4b4b4)
Te6edf3webTff7b72.Td2a8ffrun_app Tff7b72= Te6edf3_safe_run_app
Tff7b72return Te6edf3webTb4b4b4, Te6edf3original_run_app
Tff7b72def Td2a8ff_clear_stale_storage_lockTb4b4b4(Te6edf3storage_dirTb4b4b4)Tb4b4b4:
T8b949e"""Drop a leftover soft lock from a previous process death.
Android often lacks flock (ENOSYS), so StorageLock falls back to a PID
file. After force-stop / crash the old PID may still look "alive" under
Android's process model, which would make boot exit with SystemExit(1).
The Java wrapper already serializes start_server, so clearing is safe.
"""
Tff7b72if Tff7b72not Te6edf3storage_dirTb4b4b4:
Tff7b72return
Te6edf3lock_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3storage_dirTb4b4b4, Ta5d6ff"Ta5d6ff.meshchatx.lockTa5d6ff"Tb4b4b4)
Tff7b72tryTb4b4b4:
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3lock_pathTb4b4b4)Tb4b4b4:
Te6edf3osTff7b72.Td2a8ffremoveTb4b4b4(Te6edf3lock_pathTb4b4b4)
Tff7b72except Tf85149OSError Tff7b72as Te6edf3excTb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffmeshchat_wrapper: could not clear storage lock: Tffd700{Te6edf3excTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72def Td2a8ff_patch_rns_panic_for_androidTb4b4b4(Tb4b4b4)Tb4b4b4:
T8b949e"""Stop RNS.panic/os._exit from killing the whole Android process."""
Tff7b72tryTb4b4b4:
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787rns_startup_recovery Tff7b72import Tb4b4b4(
Te6edf3install_rns_panic_containmentTb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3install_rns_panic_containmentTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3excTb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffmeshchat_wrapper: RNS panic containment skipped: Tffd700{Te6edf3excTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72return Tff7b72False
Tff7b72def Td2a8ff_install_android_rnode_supportTb4b4b4(Te6edf3activityTff7b72=Tff7b72NoneTb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787android_rnode Tff7b72import Te6edf3install_android_rnode_support
Te6edf3ok Tff7b72= Te6edf3install_android_rnode_supportTb4b4b4(Te6edf3activityTb4b4b4)
Tff7b72if Te6edf3okTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffmeshchat_wrapper: Android RNode USB/Bluetooth support readyTa5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffmeshchat_wrapper: Android RNode support not fully configuredTa5d6ff"Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3excTb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffmeshchat_wrapper: Android RNode support skipped: Tffd700{Te6edf3excTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72def Td2a8ff_probe_local_status_payloadTb4b4b4(Te6edf3portTb4b4b4, Te6edf3timeoutTff7b72=T79c0ff1.5Tb4b4b4)Tb4b4b4:
Tff7b72import T7ee787httpT7ee787.T7ee787client
Tff7b72import T7ee787json
Tff7b72import T7ee787ssl
Tff7b72def Td2a8ff_fetchTb4b4b4(Te6edf3use_httpsTb4b4b4)Tb4b4b4:
Te6edf3conn Tff7b72= Tff7b72None
Tff7b72tryTb4b4b4:
Tff7b72if Te6edf3use_httpsTb4b4b4:
Te6edf3context Tff7b72= Te6edf3sslTff7b72.Td2a8ffSSLContextTb4b4b4(Te6edf3sslTff7b72.Td2a8ffPROTOCOL_TLS_CLIENTTb4b4b4)
Te6edf3contextTff7b72.Td2a8ffcheck_hostname Tff7b72= Tff7b72False
Te6edf3contextTff7b72.Td2a8ffverify_mode Tff7b72= Te6edf3sslTff7b72.Td2a8ffCERT_NONE
Te6edf3conn Tff7b72= Te6edf3httpTff7b72.Td2a8ffclientTff7b72.Td2a8ffHTTPSConnectionTb4b4b4(
Ta5d6ff"Ta5d6ff127.0.0.1Ta5d6ff"Tb4b4b4,
Te6edf3portTb4b4b4,
Te6edf3timeoutTff7b72=Te6edf3timeoutTb4b4b4,
Te6edf3contextTff7b72=Te6edf3contextTb4b4b4,
Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3conn Tff7b72= Te6edf3httpTff7b72.Td2a8ffclientTff7b72.Td2a8ffHTTPConnectionTb4b4b4(Ta5d6ff"Ta5d6ff127.0.0.1Ta5d6ff"Tb4b4b4, Te6edf3portTb4b4b4, Te6edf3timeoutTff7b72=Te6edf3timeoutTb4b4b4)
Te6edf3connTff7b72.Td2a8ffrequestTb4b4b4(Ta5d6ff"Ta5d6ffGETTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff/api/v1/statusTa5d6ff"Tb4b4b4)
Te6edf3response Tff7b72= Te6edf3connTff7b72.Td2a8ffgetresponseTb4b4b4(Tb4b4b4)
Te6edf3body Tff7b72= Te6edf3responseTff7b72.Td2a8ffreadTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3responseTff7b72.Td2a8ffstatus Tff7b72!= T79c0ff200Tb4b4b4:
Tff7b72return Tff7b72None
Tff7b72return Te6edf3jsonTff7b72.Td2a8ffloadsTb4b4b4(Te6edf3bodyTff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72return Tff7b72None
Tff7b72finallyTb4b4b4:
Tff7b72if Te6edf3conn Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72with Te6edf3contextlibTff7b72.Td2a8ffsuppressTb4b4b4(Tf85149ExceptionTb4b4b4)Tb4b4b4:
Te6edf3connTff7b72.Td2a8ffcloseTb4b4b4(Tb4b4b4)
Tff7b72return Te6edf3_fetchTb4b4b4(Tff7b72TrueTb4b4b4) Tff7b72or Te6edf3_fetchTb4b4b4(Tff7b72FalseTb4b4b4)
Tff7b72def Td2a8ff_local_backend_matchesTb4b4b4(Te6edf3portTb4b4b4, Te6edf3timeoutTff7b72=T79c0ff1.5Tb4b4b4)Tb4b4b4:
Te6edf3payload Tff7b72= Te6edf3_probe_local_status_payloadTb4b4b4(Te6edf3portTb4b4b4, Te6edf3timeoutTff7b72=Te6edf3timeoutTb4b4b4)
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3payloadTb4b4b4, Tffa657dictTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72False
Te6edf3expected_keys Tff7b72= Tb4b4b4{Ta5d6ff"Ta5d6ffstageTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffnetwork_readyTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6fflisten_portTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffhttps_enabledTa5d6ff"Tb4b4b4}
Tff7b72if Tff7b72not Te6edf3expected_keysTff7b72.Td2a8ffissubsetTb4b4b4(Te6edf3payloadTff7b72.Td2a8ffkeysTb4b4b4(Tb4b4b4)Tb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72False
Tff7b72return Te6edf3payloadTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6fflisten_portTa5d6ff"Tb4b4b4) Tff7b72== Te6edf3port
Tff7b72def Td2a8ff_wait_for_own_backend_or_free_portTb4b4b4(
Te6edf3portTb4b4b4,
Tff7b72*Tb4b4b4,
Te6edf3wait_secondsTff7b72=T79c0ff3.0Tb4b4b4,
Te6edf3poll_intervalTff7b72=T79c0ff0.3Tb4b4b4,
Te6edf3probe_timeoutTff7b72=T79c0ff1.5Tb4b4b4,
Tb4b4b4)Tb4b4b4:
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787interface_port_check Tff7b72import Te6edf3is_port_in_use
Te6edf3deadline Tff7b72= Te6edf3timeTff7b72.Td2a8ffmonotonicTb4b4b4(Tb4b4b4) Tff7b72+ Te6edf3wait_seconds
Tff7b72while Tff7b72TrueTb4b4b4:
Tff7b72if Tff7b72not Te6edf3is_port_in_useTb4b4b4(Ta5d6ff"Ta5d6ff127.0.0.1Ta5d6ff"Tb4b4b4, Te6edf3portTb4b4b4)Tb4b4b4:
Tff7b72return Ta5d6ff"Ta5d6fffreeTa5d6ff"
Tff7b72if Te6edf3_local_backend_matchesTb4b4b4(Te6edf3portTb4b4b4, Te6edf3timeoutTff7b72=Te6edf3probe_timeoutTb4b4b4)Tb4b4b4:
Tff7b72return Ta5d6ff"Ta5d6ffservingTa5d6ff"
Tff7b72if Te6edf3timeTff7b72.Td2a8ffmonotonicTb4b4b4(Tb4b4b4) Tff7b72>Tff7b72= Te6edf3deadlineTb4b4b4:
Tff7b72return Ta5d6ff"Ta5d6ffbusyTa5d6ff"
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(Te6edf3poll_intervalTb4b4b4)
Tff7b72def Td2a8ffstart_serverTb4b4b4(Te6edf3portTff7b72=T79c0ff8000Tb4b4b4, Te6edf3app_files_dirTff7b72=Tff7b72NoneTb4b4b4, Te6edf3activityTff7b72=Tff7b72NoneTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3_server_loop_active
Tff7b72with Te6edf3_server_loop_lockTb4b4b4:
Tff7b72if Te6edf3_server_loop_activeTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ffmeshchat_wrapper: start_server ignored (server loop already active)Ta5d6ff"Tb4b4b4)
Tff7b72return
Te6edf3_server_loop_active Tff7b72= Tff7b72True
Tff7b72tryTb4b4b4:
Te6edf3storage_dir Tff7b72= Tff7b72None
Te6edf3reticulum_config_dir Tff7b72= Tff7b72None
Tff7b72if Te6edf3app_files_dirTb4b4b4:
Te6edf3base_dir Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3app_files_dirTb4b4b4, Ta5d6ff"Ta5d6ffmeshchatxTa5d6ff"Tb4b4b4)
Te6edf3storage_dir Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3base_dirTb4b4b4, Ta5d6ff"Ta5d6ffstorageTa5d6ff"Tb4b4b4)
Te6edf3reticulum_config_dir Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3base_dirTb4b4b4, Ta5d6ff"Ta5d6ffreticulumTa5d6ff"Tb4b4b4)
Te6edf3osTff7b72.Td2a8ffmakedirsTb4b4b4(Te6edf3storage_dirTb4b4b4, Te6edf3exist_okTff7b72=Tff7b72TrueTb4b4b4)
Te6edf3osTff7b72.Td2a8ffmakedirsTb4b4b4(Te6edf3reticulum_config_dirTb4b4b4, Te6edf3exist_okTff7b72=Tff7b72TrueTb4b4b4)
Te6edf3_ensure_android_reticulum_configTb4b4b4(Te6edf3reticulum_config_dirTb4b4b4)
Te6edf3_clear_stale_storage_lockTb4b4b4(Te6edf3storage_dirTb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3port_outcome Tff7b72= Te6edf3_wait_for_own_backend_or_free_portTb4b4b4(Te6edf3portTb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3port_check_excTb4b4b4:
Te6edf3port_outcome Tff7b72= Ta5d6ff"Ta5d6fffreeTa5d6ff"
Tffa657printTb4b4b4(
Ta5d6fffTa5d6ff"Ta5d6ffmeshchat_wrapper: port availability check skipped: Tffd700{Te6edf3port_check_excTffd700}Ta5d6ff"
Tb4b4b4)
Tff7b72if Te6edf3port_outcome Tff7b72== Ta5d6ff"Ta5d6ffservingTa5d6ff"Tb4b4b4:
Tffa657printTb4b4b4(
Ta5d6fffTa5d6ff"Ta5d6ffmeshchat_wrapper: MeshChatX backend already serving on port Tffd700{Te6edf3portTffd700}Ta5d6ff, Ta5d6ff"
Ta5d6ff"Ta5d6ffskipping duplicate start_server (activity was likely recreated)Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72return
Tff7b72if Te6edf3port_outcome Tff7b72== Ta5d6ff"Ta5d6ffbusyTa5d6ff"Tb4b4b4:
Tffa657printTb4b4b4(
Ta5d6fffTa5d6ff"Ta5d6ffmeshchat_wrapper: port Tffd700{Te6edf3portTffd700}Ta5d6ff is still occupied by another process Ta5d6ff"
Ta5d6ff"Ta5d6ffafter waiting, attempting to start anywayTa5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3original_signal Tff7b72= Te6edf3signalTff7b72.Td2a8ffsignal
Tff7b72def Td2a8ff_safe_signalTb4b4b4(Te6edf3sigTb4b4b4, Te6edf3handlerTb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Tff7b72return Te6edf3original_signalTb4b4b4(Te6edf3sigTb4b4b4, Te6edf3handlerTb4b4b4)
Tff7b72except Tf85149ValueError Tff7b72as Te6edf3excTb4b4b4:
Tff7b72if Ta5d6ff"Ta5d6ffmain thread of the main interpreterTa5d6ff" Tff7b72in Tffa657strTb4b4b4(Te6edf3excTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72None
Tff7b72raise
Te6edf3signalTff7b72.Td2a8ffsignal Tff7b72= Te6edf3_safe_signal
Te6edf3asyncio_signal_patch Tff7b72= Te6edf3_patch_asyncio_signal_handlers_for_androidTb4b4b4(Tb4b4b4)
Te6edf3aiohttp_run_app_patch Tff7b72= Te6edf3_patch_aiohttp_run_app_for_androidTb4b4b4(Tb4b4b4)
Te6edf3_patch_rns_panic_for_androidTb4b4b4(Tb4b4b4)
T8b949e# Codec2 must be ready before any import that pulls LXST.Codecs (soft-import
T8b949e# locks Codec2=None for the process if pycodec2 fails on first import).
Tff7b72tryTb4b4b4:
Tff7b72from T7ee787meshchatxT7ee787.T7ee787android_codec2 Tff7b72import Tb4b4b4(
Te6edf3ensure_codec2_native_libraryTb4b4b4,
Te6edf3ensure_lxst_codec2_bindingTb4b4b4,
Te6edf3probe_pycodec2Tb4b4b4,
Tb4b4b4)
Te6edf3ensure_codec2_native_libraryTb4b4b4(Te6edf3forceTff7b72=Tff7b72TrueTb4b4b4)
Te6edf3okTb4b4b4, Te6edf3err Tff7b72= Te6edf3probe_pycodec2Tb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3okTb4b4b4:
Te6edf3bound Tff7b72= Te6edf3ensure_lxst_codec2_bindingTb4b4b4(Tb4b4b4)
Tffa657printTb4b4b4(
Ta5d6ff"Ta5d6ffmeshchat_wrapper: Codec2/pycodec2 readyTa5d6ff"
Tff7b72+ Tb4b4b4(Ta5d6ff"Ta5d6ff (LXST bound)Ta5d6ff" Tff7b72if Te6edf3bound Tff7b72else Ta5d6ff"Ta5d6ff (LXST bind deferred)Ta5d6ff"Tb4b4b4)
Tb4b4b4)
Tff7b72elseTb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffmeshchat_wrapper: Codec2/pycodec2 unavailable: Tffd700{Te6edf3errTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3codec2_excTb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffmeshchat_wrapper: Codec2 preload skipped: Tffd700{Te6edf3codec2_excTffd700}Ta5d6ff"Tb4b4b4)
Te6edf3_install_android_rnode_supportTb4b4b4(Te6edf3activityTb4b4b4)
Tff7b72from T7ee787meshchatxT7ee787.T7ee787meshchat Tff7b72import Te6edf3ReticulumMeshChatTb4b4b4, Te6edf3main
Tff7b72tryTb4b4b4:
Tff7b72from T7ee787meshchatxT7ee787.T7ee787android_codec2 Tff7b72import Te6edf3ensure_lxst_codec2_binding
Tff7b72if Te6edf3ensure_lxst_codec2_bindingTb4b4b4(Tb4b4b4)Tb4b4b4:
Tffa657printTb4b4b4(
Ta5d6ff"Ta5d6ffmeshchat_wrapper: LXST Codec2 binding confirmed after meshchat importTa5d6ff"
Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3bind_excTb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffmeshchat_wrapper: LXST Codec2 bind skipped: Tffd700{Te6edf3bind_excTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72tryTb4b4b4:
Tff7b72from T7ee787meshchatxT7ee787.T7ee787android_push_bridge Tff7b72import Te6edf3install_websocket_hook
Te6edf3install_websocket_hookTb4b4b4(Te6edf3ReticulumMeshChatTb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3hook_excTb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffmeshchat_wrapper: install_websocket_hook skipped: Tffd700{Te6edf3hook_excTffd700}Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffargv Tff7b72= Tb4b4b4[
Ta5d6ff"Ta5d6ffmeshchatTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff--headlessTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff--hostTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff127.0.0.1Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff--portTa5d6ff"Tb4b4b4,
Tffa657strTb4b4b4(Te6edf3portTb4b4b4)Tb4b4b4,
Tb4b4b4]
Tff7b72if Te6edf3storage_dirTb4b4b4:
Te6edf3sysTff7b72.Td2a8ffargvTff7b72.Td2a8ffextendTb4b4b4(Tb4b4b4[Ta5d6ff"Ta5d6ff--storage-dirTa5d6ff"Tb4b4b4, Te6edf3storage_dirTb4b4b4]Tb4b4b4)
Tff7b72if Te6edf3reticulum_config_dirTb4b4b4:
Te6edf3sysTff7b72.Td2a8ffargvTff7b72.Td2a8ffextendTb4b4b4(Tb4b4b4[Ta5d6ff"Ta5d6ff--reticulum-config-dirTa5d6ff"Tb4b4b4, Te6edf3reticulum_config_dirTb4b4b4]Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3mainTb4b4b4(Tb4b4b4)
Tff7b72finallyTb4b4b4:
Te6edf3signalTff7b72.Td2a8ffsignal Tff7b72= Te6edf3original_signal
Tff7b72if Te6edf3asyncio_signal_patch Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Te6edf3loop_clsTb4b4b4, Te6edf3original_add_signal_handler Tff7b72= Te6edf3asyncio_signal_patch
Te6edf3loop_clsTff7b72.Td2a8ffadd_signal_handler Tff7b72= Te6edf3original_add_signal_handler
Tff7b72if Te6edf3aiohttp_run_app_patch Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Te6edf3web_moduleTb4b4b4, Te6edf3original_run_app Tff7b72= Te6edf3aiohttp_run_app_patch
Te6edf3web_moduleTff7b72.Td2a8ffrun_app Tff7b72= Te6edf3original_run_app
Tff7b72except Tf85149SystemExit Tff7b72as Te6edf3eTb4b4b4:
T8b949e# Chaquopy surfaces SystemExit as PyException; re-raise as RuntimeError
T8b949e# so Java retry/error UI gets a readable message.
Te6edf3code Tff7b72= Tffa657getattrTb4b4b4(Te6edf3eTb4b4b4, Ta5d6ff"Ta5d6ffcodeTa5d6ff"Tb4b4b4, Te6edf3eTb4b4b4)
Te6edf3cause Tff7b72= Te6edf3eTff7b72.Td2a8ff__cause__ Tff7b72or Te6edf3eTff7b72.Td2a8ff__context__
Te6edf3cause_text Tff7b72= Tffa657strTb4b4b4(Te6edf3causeTb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4) Tff7b72if Te6edf3cause Tff7b72is Tff7b72not Tff7b72None Tff7b72else Ta5d6ff"Ta5d6ff"
Tff7b72if Te6edf3cause_textTb4b4b4:
Te6edf3message Tff7b72= Ta5d6fffTa5d6ff"Ta5d6ffMeshChatX exited during startup (code=Tffd700{Te6edf3codeTffd700}Ta5d6ff): Tffd700{Te6edf3cause_textTffd700}Ta5d6ff"
Tff7b72elseTb4b4b4:
Te6edf3message Tff7b72= Ta5d6fffTa5d6ff"Ta5d6ffMeshChatX exited during startup (code=Tffd700{Te6edf3codeTffd700}Ta5d6ff)Ta5d6ff"
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffError starting MeshChatX server: Tffd700{Te6edf3messageTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72raise Tf85149RuntimeErrorTb4b4b4(Te6edf3messageTb4b4b4) Tff7b72from T7ee787e
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tffa657printTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffError starting MeshChatX server: Tffd700{Te6edf3eTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72import T7ee787traceback
Te6edf3tracebackTff7b72.Td2a8ffprint_excTb4b4b4(Tb4b4b4)
Tff7b72raise
Tff7b72finallyTb4b4b4:
Tff7b72with Te6edf3_server_loop_lockTb4b4b4:
Te6edf3_server_loop_active Tff7b72= Tff7b72False
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────